#include "ComposantCouleur.hpp"

#include "Utils/ATL.hpp"
#include "Utils/borne.hpp"

using namespace std;

const unsigned ComposantCouleur::NbrBytes( 1 );

int ComposantCouleur::MaxComposante( 0 );

/**
 * Permet de récupérer la valeur maximale d'une composante.
 *
 * Lors du premier appel, la valeur de MaxComposante est calculée à
 * partir de la valeur de NbrBytes.
 *
 * @return une référence constante sur MaxComposante.
 */
const int& ComposantCouleur::maxComposante()
{
  if ( MaxComposante == 0 )
    MaxComposante = puissance< NbrBytes * 8u >( 2 );

  return MaxComposante;
}

/**
 * Permet d'accéder à la valeur de NbrBytes.
 *
 * @return une référence constante sur NbrBytes.
 */
const unsigned& ComposantCouleur::nbrBytes()
{
  return NbrBytes;
}

/**
 * Constructeur standard, initialise la valeur de la composante à
 * partir d'un nombre entier. Ce nombre est automatiquement ramené
 * dans le bon intervalle.
 *
 * @param[in] entier nombre entier qui représente la valeur de la
 * composante.
 */
ComposantCouleur::ComposantCouleur( const int& entier )
  : Valeur( entier )
{
  Valeur = borne( Valeur, 0, maxComposante() );
}

/**
 * Constructeur de copie, permettant de dupliquer une instance de
 * ComposantCouleur.
 *
 * @param[in] autre instance de ComposantCouleur à copier.
 */
ComposantCouleur::ComposantCouleur( const ComposantCouleur& autre )
  : Valeur( autre.Valeur )
{}

/**
 * Destructeur, qui n'a rien de spécial à faire.
 */
ComposantCouleur::~ComposantCouleur()
{}

/**
 * Opérateur de conversion en entier non signé.
 *
 * @return la valeur de la composante, écrite dans un unsigned.
 */
ComposantCouleur::operator unsigned() const
{
  return static_cast< unsigned >( Valeur );
}

/**
 * Opérateur de conversion en entier signé.
 *
 * @return la valeur de la composante, écrite dans un int.
 */
ComposantCouleur::operator int() const
{
  return Valeur;
}

/**
 * Opérateur de conversion en un nombre réel.
 *
 * @return la valeur de la composante, écrite dans un double.
 */
ComposantCouleur::operator double() const
{
  return static_cast< double >( Valeur );
}

/**
 * Opérateur de conversion en caractère non signé.
 *
 * Comme le format unsigned char n'a une profondeur de d'un byte, il
 * faut tronquer la valeur si la profondeur de l'image est plus
 * grande.
 *
 * @return la valeur de la composante, écrite dans un unsigned char.
 */
ComposantCouleur::operator unsigned char() const
{
  return static_cast< unsigned char >( Valeur % ( puissance< NbrBytes >( maxComposante() ) ) );
}

/**
 * Opérateur de comparaison.
 *
 * @param[in] autre instance de ComposantCouleur avec laquelle
 * l'instance courante sera comparée.
 *
 * @return \e true si les deux valeurs sont égales, \e false sinon.
 */
bool ComposantCouleur::operator==( const ComposantCouleur& autre ) const
{
  return Valeur == autre.Valeur;
}

/**
 * Opérateur de comparaison "est plus petit que".
 *
 * @param[in] autre instance de ComposantCouleur avec laquelle
 * l'instance courante sera comparée.
 *
 * @return \e true si la valeur de l'instance courante est plus petite
 * que celle de l'instance \c autre.
 */
bool ComposantCouleur::operator<=( const ComposantCouleur& autre ) const
{
  return Valeur <= autre.Valeur;
}

/**
 * Opérateur de comparaison "est plus grand que".
 *
 * @param[in] autre instance de ComposantCouleur avec laquelle
 * l'instance courante sera comparée.
 *
 * @return \e true si la valeur de l'instance courante est plus grande
 * que celle de l'instance \c autre.
 */
bool ComposantCouleur::operator>=( const ComposantCouleur& autre ) const
{
  return Valeur >= autre.Valeur;
}

/**
 * Opérateur de comparaison "est strictement plus petit que".
 *
 * @param[in] autre instance de ComposantCouleur avec laquelle
 * l'instance courante sera comparée.
 *
 * @return \e true si la valeur de l'instance courante est plus petite
 * que celle de l'instance \c autre.
 */
bool ComposantCouleur::operator<( const ComposantCouleur& autre ) const
{
  return Valeur < autre.Valeur;
}

/**
 * Opérateur de comparaison "est strictement plus grand que".
 *
 * @param[in] autre instance de ComposantCouleur avec laquelle
 * l'instance courante sera comparée.
 *
 * @return \e true si la valeur de l'instance courante est plus grande
 * que celle de l'instance \c autre.
 */
bool ComposantCouleur::operator>( const ComposantCouleur& autre ) const
{
  return Valeur > autre.Valeur;
}

/**
 * Opérateur d'affectation, permet de copier le contenu d'une instance
 * dans l'instance courante.
 *
 * @param[in] autre instance de ComposantCouleur que l'on veut copier.
 *
 * @return une référence sur l'instance courante.
 */
ComposantCouleur& ComposantCouleur::operator=( const ComposantCouleur& autre )
{
  if ( &autre != this )
    Valeur = autre.Valeur;

  return *this;
}

/**
 * Opérateur d'addition, modifiant l'instance courante.
 *
 * Il faut surveiller ici que la valeur finale est bien dans
 * l'intervalle autorisé.
 *
 * @param[in] autre instance dont on veut additionner la valeur à
 * l'instance courante.
 *
 * @return une référence sur l'instance courante.
 */
ComposantCouleur& ComposantCouleur::operator+=( const ComposantCouleur& autre )
{
  Valeur += autre.Valeur;
  Valeur = borne( Valeur, 0, maxComposante() );

  return *this;
}

/**
 * Opérateur de soustraction, modifiant l'instance courante.
 *
 * Il faut surveiller ici que la valeur finale est bien dans
 * l'intervalle autorisé.
 *
 * @param[in] autre instance dont on veut soustraire la valeur à
 * l'instance courante.
 *
 * @return une référence sur l'instance courante.
 */
ComposantCouleur& ComposantCouleur::operator-=( const ComposantCouleur& autre )
{
  Valeur -= autre.Valeur;
  Valeur = borne( Valeur, 0, maxComposante() );

  return *this;
}

/**
 * Opérateur d'addition, qui stocke le résultat dans une nouvelle
 * instance.
 *
 * Il faut surveiller ici que la valeur finale est bien dans
 * l'intervalle autorisé. La surveillance est déléguée au constructeur
 * de la nouvelle instance.
 *
 * @param[in] autre instance dont on veut additionner la valeur à
 * l'instance courante.
 *
 * @return une nouvelle instance.
 */
ComposantCouleur ComposantCouleur::operator+( const ComposantCouleur& autre ) const
{
  ComposantCouleur tmp( Valeur + autre.Valeur );

  return tmp;
}

/**
 * Opérateur de soustraction, qui stocke le résultat dans une nouvelle
 * instance.
 *
 * Il faut surveiller ici que la valeur finale est bien dans
 * l'intervalle autorisé. La surveillance est déléguée au constructeur
 * de la nouvelle instance.
 *
 * @param[in] autre instance dont on veut soustraire la valeur à
 * l'instance courante.
 *
 * @return une nouvelle instance.
 */
ComposantCouleur ComposantCouleur::operator-( const ComposantCouleur& autre ) const
{
  ComposantCouleur tmp( Valeur - autre.Valeur );

  return tmp;
}

/**
 * Opérateur de multiplication par un scalaire réel, modifiant
 * l'instance courante.
 *
 * Il faut surveiller ici que la valeur finale est bien dans
 * l'intervalle autorisé.
 *
 * @param[in] scalaire nombre réel par lequel on veut multiplier la
 * valeur de la composante.
 *
 * @return une référence sur l'instance courante.
 */
ComposantCouleur& ComposantCouleur::operator*=( const double& scalaire )
{
  double tmp( Valeur );
  tmp *= scalaire;
  Valeur = static_cast< int >( tmp );

  Valeur = borne( Valeur, 0, maxComposante() );

  return *this;
}

/**
 * Opérateur d'affectation, permet de promouvoir un entier en
 * ComposantCouleur.
 *
 * @param[in] entier nombre entier que l'on veut transformer en
 * ComposantCouleur.
 *
 * @return une référence sur l'instance courante.
 */
ComposantCouleur& ComposantCouleur::operator=( const int& entier )
{
  Valeur = entier;
  Valeur = borne( Valeur, 0, maxComposante() );

  return *this;
}

ostream& operator<<( ostream& os, const ComposantCouleur& comp )
{
  return os << comp.Valeur;
}
